from django.shortcuts import render, redirect, get_object_or_404
import logging
from django.views import View
from django.views.generic import ListView, CreateView, DeleteView, DetailView, UpdateView, TemplateView
from django.urls import reverse_lazy, reverse
from django.http import JsonResponse, HttpResponseRedirect, HttpResponse
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from .models import Server, CDNDomain, RequestUrl, AutoTask, TaskExecutionResult, ManualPreheat
from .forms import ServerForm, CDNDomainForm, RequestUrlForm, AutoTaskForm, ManualPreheatForm, UserCreationForm, UserUpdateForm, UserPasswordChangeForm
import paramiko
import requests
import threading
import time
from django.utils import timezone
import json

logger = logging.getLogger(__name__)

# 服务器管理视图
class ServerListView(LoginRequiredMixin, ListView):
    model = Server
    template_name = 'preheat/server_list.html'
    context_object_name = 'servers'
    ordering = ['-created_time']

class ServerCreateView(LoginRequiredMixin, CreateView):
    model = Server
    form_class = ServerForm
    template_name = 'preheat/server_create.html'
    success_url = reverse_lazy('preheat:server_list')
    
    def form_valid(self, form):
        # 保存表单数据
        self.object = form.save()
        
        # 准备返回的数据
        data = {
            'id': self.object.id,
            'ip': self.object.ip,
            'ssh_port': self.object.ssh_port,
            'remark': self.object.remark,
            'created_time': self.object.created_time.strftime('%Y-%m-%d %H:%M:%S') if self.object.created_time else ''
        }
        
        # 如果是AJAX请求，返回JSON响应
        if self.request.headers.get('x-requested-with') == 'XMLHttpRequest':
            response_data = {
                'success': True,
                'message': '服务器添加成功！',
                'data': data
            }
            return JsonResponse(response_data)
        
        # 普通表单提交，显示消息并重定向
        messages.success(self.request, '服务器添加成功！')
        return super().form_valid(form)
    
    def form_invalid(self, form):
        # 如果是AJAX请求，返回JSON错误响应
        if self.request.headers.get('x-requested-with') == 'XMLHttpRequest':
            errors = {}
            for field in form:
                for error in field.errors:
                    errors[field.name] = errors.get(field.name, []) + [str(error)]
            
            # 添加非字段错误
            if form.non_field_errors():
                errors['__all__'] = [str(error) for error in form.non_field_errors()]
            
            response_data = {
                'success': False,
                'message': '表单验证失败，请检查输入',
                'errors': errors
            }
            return JsonResponse(response_data, status=400)
        
        # 普通表单提交，返回默认响应
        return super().form_invalid(form)

class ServerDeleteView(LoginRequiredMixin, DeleteView):
    model = Server
    template_name = 'preheat/server_confirm_delete.html'
    success_url = reverse_lazy('preheat:server_list')
    
    def delete(self, request, *args, **kwargs):
        messages.success(self.request, '服务器删除成功！')
        return super().delete(request, *args, **kwargs)

# CDN域名管理视图
class CDNDomainListView(LoginRequiredMixin, ListView):
    model = CDNDomain
    template_name = 'preheat/cdn_domain_list.html'
    context_object_name = 'domains'
    ordering = ['-created_time']

class CDNDomainCreateView(LoginRequiredMixin, CreateView):
    model = CDNDomain
    form_class = CDNDomainForm
    template_name = 'preheat/cdn_domain_create.html'
    success_url = reverse_lazy('preheat:cdn_domain_list')
    
    def form_valid(self, form):
        messages.success(self.request, 'CDN域名添加成功！')
        return super().form_valid(form)

class CDNDomainDeleteView(LoginRequiredMixin, DeleteView):
    model = CDNDomain
    template_name = 'preheat/cdn_domain_confirm_delete.html'
    success_url = reverse_lazy('preheat:cdn_domain_list')
    
    def delete(self, request, *args, **kwargs):
        messages.success(self.request, 'CDN域名删除成功！')
        return super().delete(request, *args, **kwargs)

# 请求地址管理视图
class RequestUrlListView(LoginRequiredMixin, ListView):
    model = RequestUrl
    template_name = 'preheat/request_url_list.html'
    context_object_name = 'urls'
    ordering = ['-created_time']
    
    def get_queryset(self):
        # 获取所有请求地址
        queryset = super().get_queryset()
        
        # 从请求参数中获取选中的CDN域名ID
        cdn_domain_id = self.request.GET.get('cdn_domain')
        
        # 如果提供了域名ID，则根据该域名筛选请求地址
        if cdn_domain_id:
            try:
                cdn_domain_id_int = int(cdn_domain_id)
                queryset = queryset.filter(cdn_domain_id=cdn_domain_id_int)
            except ValueError:
                # 无效的域名ID，忽略筛选条件
                pass
        
        return queryset
        
    def get_context_data(self, **kwargs):
        # 获取默认上下文数据
        context = super().get_context_data(**kwargs)
        
        # 添加所有CDN域名到上下文，用于筛选下拉框
        context['cdn_domains'] = CDNDomain.objects.all().order_by('domain')
        
        # 添加当前选中的域名ID到上下文
        context['selected_domain'] = self.request.GET.get('cdn_domain')
        
        return context

class RequestUrlCreateView(LoginRequiredMixin, CreateView):
    model = RequestUrl
    form_class = RequestUrlForm
    template_name = 'preheat/request_url_create.html'
    success_url = reverse_lazy('preheat:request_url_list')
    
    def get_form_kwargs(self):
        # 不需要传递添加模式参数，表单已经固定为批量添加模式
        kwargs = super().get_form_kwargs()
        return kwargs
        
    def form_invalid(self, form):
        # 直接渲染响应
        context = {'form': form}
        return self.render_to_response(context)
    
    def form_valid(self, form):
        # 获取CDN域名
        cdn_domain = form.cleaned_data['cdn_domain']
        
        # 获取批量URL列表
        batch_urls = form.cleaned_data['batch_urls']
        
        # 批量创建URL记录
        created_count = 0
        for url_path in batch_urls:
            # 检查URL是否已存在
            if not RequestUrl.objects.filter(url=url_path, cdn_domain=cdn_domain).exists():
                RequestUrl.objects.create(url=url_path, cdn_domain=cdn_domain)
                created_count += 1
        
        if created_count > 0:
            messages.success(self.request, f'成功添加了 {created_count} 个请求地址！')
        else:
            messages.warning(self.request, '所有URL已存在，未添加任何新的请求地址')
        
        return redirect(self.success_url)

class RequestUrlDeleteView(LoginRequiredMixin, DeleteView):
    model = RequestUrl
    template_name = 'preheat/request_url_confirm_delete.html'
    success_url = reverse_lazy('preheat:request_url_list')
    
    def delete(self, request, *args, **kwargs):
        messages.success(self.request, '请求地址删除成功！')
        return super().delete(request, *args, **kwargs)

from django.db import IntegrityError

class RequestUrlBatchDeleteView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        # 检查请求方法
        if request.method != 'POST':
            messages.error(request, '只接受POST请求')
            return redirect('preheat:request_url_list')
        
        # 检查是否有CSRF令牌
        if not request.POST.get('csrfmiddlewaretoken'):
            messages.error(request, 'CSRF令牌缺失')
            return redirect('preheat:request_url_list')
        
        # 获取选中的ID列表
        selected_ids = request.POST.getlist('selected_ids')
        
        if not selected_ids:
            messages.warning(request, '请至少选择一个请求地址进行删除')
            return redirect('preheat:request_url_list')
        
        try:
            # 尝试将ID转换为整数列表（清理输入数据）
            selected_ids_int = [int(id_str) for id_str in selected_ids]
            
            # 验证ID是否存在于数据库中
            existing_ids = RequestUrl.objects.filter(id__in=selected_ids_int).values_list('id', flat=True)
            existing_ids_list = list(existing_ids)
            
            if not existing_ids_list:
                messages.warning(request, '没有找到与选中ID匹配的请求地址')
                return redirect('preheat:request_url_list')
            
            # 批量删除请求地址
            deleted_count = RequestUrl.objects.filter(id__in=existing_ids_list).delete()[0]
            
            messages.success(request, f'成功删除了 {deleted_count} 个请求地址！')
        except ValueError:
            messages.error(request, '无效的请求地址ID')
        except IntegrityError:
            messages.error(request, '删除请求地址时发生数据库完整性错误')
        except Exception as e:
            messages.error(request, f'删除失败: {str(e)}')
            
        return redirect('preheat:request_url_list')

# 自动任务管理视图
class AutoTaskListView(LoginRequiredMixin, ListView):
    model = AutoTask
    template_name = 'preheat/auto_task_list.html'
    context_object_name = 'tasks'
    ordering = ['-created_time']

class AutoTaskCreateView(LoginRequiredMixin, CreateView):
    model = AutoTask
    form_class = AutoTaskForm
    template_name = 'preheat/auto_task_create.html'
    success_url = reverse_lazy('preheat:auto_task_list')
    
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        # 传递request对象给表单，以便处理筛选逻辑
        kwargs['request'] = self.request
        return kwargs
        
    def get_initial(self):
        initial = super().get_initial()
        # 如果URL中包含cdn_domain参数，设置为初始值
        if 'cdn_domain' in self.request.GET:
            initial['cdn_domain_filter'] = self.request.GET['cdn_domain']
        return initial
    
    def form_valid(self, form):
        # 先保存表单数据
        response = super().form_valid(form)
        
        # 获取新创建的任务ID
        task_id = self.object.id
        
        # 尝试将任务添加到调度器
        try:
            add_task_to_scheduler(task_id)
            messages.success(self.request, '自动任务添加成功，并已添加到调度器！')
        except Exception as e:
            messages.success(self.request, '自动任务添加成功！')
            messages.warning(self.request, f'尝试将任务添加到调度器时发生错误: {str(e)}')
            # 继续执行成功响应，不因为调度器错误而中断
        
        return response
        
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 从GET参数或表单初始值获取当前选中的CDN域名ID
        cdn_domain_id = self.request.GET.get('cdn_domain') or context['form'].initial.get('cdn_domain_filter')
        
        # 获取所有CDN域名
        all_domains = CDNDomain.objects.all().order_by('domain')
        
        # 如果有选中的CDN域名，获取该域名下的请求地址
        if cdn_domain_id:
            try:
                cdn_domain_id_int = int(cdn_domain_id)
                # 获取选中域名下的请求地址
                domain_request_urls = RequestUrl.objects.filter(cdn_domain_id=cdn_domain_id_int)
                context['domain_request_count'] = domain_request_urls.count()
                # 获取所有CDN域名及其请求地址数量
                context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
                context['selected_domain'] = CDNDomain.objects.get(id=cdn_domain_id_int)
            except (ValueError, CDNDomain.DoesNotExist):
                context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
        else:
            # 获取所有CDN域名及其请求地址数量
            context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
        
        return context

from preheat.scheduler import remove_job_from_scheduler, add_task_to_scheduler

class AutoTaskDeleteView(LoginRequiredMixin, DeleteView):
    model = AutoTask
    template_name = 'preheat/auto_task_confirm_delete.html'
    success_url = reverse_lazy('preheat:auto_task_list')
    
    def delete(self, request, *args, **kwargs):
        # 获取要删除的任务对象
        self.object = self.get_object()
        task_id = self.object.id
        
        # 先从调度器中删除作业
        try:
            remove_job_from_scheduler(task_id)
        except Exception as e:
            messages.warning(self.request, f'尝试从调度器中删除作业时发生错误: {str(e)}')
            # 继续执行删除操作，不因为调度器错误而中断
        
        # 删除任务记录
        response = super().delete(request, *args, **kwargs)
        messages.success(self.request, '自动任务删除成功！')
        return response

class AutoTaskDetailView(LoginRequiredMixin, DetailView):
    model = AutoTask
    template_name = 'preheat/auto_task_detail.html'
    context_object_name = 'task'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['execution_results'] = TaskExecutionResult.objects.filter(task=self.object).order_by('-execution_time')
        return context

# 手动预热视图
class ManualPreheatCreateView(LoginRequiredMixin, CreateView):
    model = ManualPreheat
    form_class = ManualPreheatForm
    template_name = 'preheat/manual_preheat_create.html'
    
    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        # 传递request对象给表单，以便处理筛选逻辑
        kwargs['request'] = self.request
        return kwargs
        
    def get_initial(self):
        initial = super().get_initial()
        # 如果URL中包含cdn_domain参数，设置为初始值
        if 'cdn_domain' in self.request.GET:
            initial['cdn_domain_filter'] = self.request.GET['cdn_domain']
        return initial
        
    def form_valid(self, form):
        # 创建预热任务对象，但不保存多对多关系
        manual_preheat = form.save(commit=False)
        
        # 保存任务名称和描述
        manual_preheat.name = form.cleaned_data['name']
        manual_preheat.description = form.cleaned_data['description']
        manual_preheat.save()
        
        # 保存多对多关系
        form.save_m2m()
        
        messages.success(self.request, '预热任务已创建，正在执行中...')
        # 启动异步任务执行预热
        threading.Thread(target=execute_manual_preheat, args=(manual_preheat.id,)).start()
        return redirect(reverse('preheat:manual_preheat_status', args=[manual_preheat.id]))
        
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 从GET参数或表单初始值获取当前选中的CDN域名ID
        cdn_domain_id = self.request.GET.get('cdn_domain') or context['form'].initial.get('cdn_domain_filter')
        
        # 获取所有CDN域名
        all_domains = CDNDomain.objects.all().order_by('domain')
        
        # 如果有选中的CDN域名，获取该域名下的请求地址
        if cdn_domain_id:
            try:
                cdn_domain_id_int = int(cdn_domain_id)
                # 获取选中域名下的请求地址
                domain_request_urls = RequestUrl.objects.filter(cdn_domain_id=cdn_domain_id_int)
                context['domain_request_count'] = domain_request_urls.count()
                # 获取所有CDN域名及其请求地址数量
                context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
                context['selected_domain'] = CDNDomain.objects.get(id=cdn_domain_id_int)
            except (ValueError, CDNDomain.DoesNotExist):
                context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
        else:
            # 获取所有CDN域名及其请求地址数量
            context['domains_with_count'] = [(domain, RequestUrl.objects.filter(cdn_domain=domain).count()) for domain in all_domains]
        
        return context

class ManualPreheatStatusView(LoginRequiredMixin, DetailView):
    model = ManualPreheat
    template_name = 'preheat/manual_preheat_status.html'
    context_object_name = 'preheat_task'

# 执行预热操作的函数
def execute_manual_preheat(preheat_id):
    import logging
    logger = logging.getLogger(__name__)
    
    preheat = get_object_or_404(ManualPreheat, id=preheat_id)
    preheat.status = 'running'
    preheat.save()
    
    failed_urls = []
    
    # 获取所有需要预热的URL
    urls_to_preheat = []
    for request_url in preheat.request_urls.all():
        full_url = f"{request_url.cdn_domain.domain}{request_url.url}"
        urls_to_preheat.append(full_url)
    
    # 记录任务开始执行的日志
    logger.info(f"Starting preheat task {preheat.id} with {len(urls_to_preheat)} URLs on {preheat.servers.count()} servers")
    
    # 在所有选择的服务器上执行预热
    for server in preheat.servers.all():
        try:
            # 使用paramiko进行SSH连接
            client = paramiko.SSHClient()
            client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            logger.info(f"Attempting SSH connection to {server.ip}:{server.ssh_port}")
            client.connect(
                hostname=server.ip,
                port=server.ssh_port,
                username='root',  # 假设使用root用户，实际使用时可能需要修改
                password=server.ssh_password
            )
            
            logger.info(f"SSH connection successful to {server.ip}:{server.ssh_port}")
            
            # 在服务器上创建临时目录
            stdin, stdout, stderr = client.exec_command('mkdir -p /tmp/cdn_preheat && cd /tmp/cdn_preheat')
            
            # 下载每个URL
            for url in urls_to_preheat:
                try:
                    # 先获取HTTP状态码
                    status_code_command = f'curl -o /dev/null -s -w "%{{http_code}}" "{url}"'
                    logger.info(f"Checking HTTP status code for {url} on {server.ip}")
                    stdin_status, stdout_status, stderr_status = client.exec_command(status_code_command)
                    status_code = stdout_status.read().decode('utf-8').strip()
                    
                    # 检查状态码是否符合要求
                    if status_code in ['200', '206', '304']:
                        # 状态码符合要求，执行下载
                        download_command = f'wget -q "{url}" -O /dev/null'
                        logger.info(f"Downloading {url} on {server.ip} with status code {status_code}")
                        stdin_download, stdout_download, stderr_download = client.exec_command(download_command)
                        exit_code = stdout_download.channel.recv_exit_status()
                        
                        if exit_code != 0:
                            error_msg = stderr_download.read().decode('utf-8')
                            logger.error(f"Download failed on {server.ip}: {error_msg}")
                            failed_urls.append(f"{server.ip}:{url} (Download Error: {error_msg})")
                        else:
                            logger.info(f"Successfully downloaded {url} on {server.ip}")
                    else:
                        # 状态码不符合要求，记录异常
                        logger.warning(f"Skipping {url} on {server.ip} due to status code {status_code}")
                        failed_urls.append(f"{server.ip}:{url} (Abnormal Status Code: {status_code})")
                except Exception as e:
                    logger.exception(f"Exception when downloading {url} on {server.ip}")
                    failed_urls.append(f"{server.ip}:{url} (Exception: {str(e)})")
            
            # 关闭SSH连接
            client.close()
        except Exception as e:
            logger.exception(f"SSH connection failed to {server.ip}:{server.ssh_port}")
            for url in urls_to_preheat:
                failed_urls.append(f"{server.ip}:{url} (SSH Connection Error: {str(e)})")
    
    # 更新预热任务状态
    preheat.status = 'completed'
    preheat.completed_time = timezone.now()
    if failed_urls:
        preheat.failed_urls = '\n'.join(failed_urls)
        logger.warning(f"Preheat task {preheat.id} completed with {len(failed_urls)} failed URLs")
    else:
        logger.info(f"Preheat task {preheat.id} completed successfully")
    preheat.save()

# 检查预热状态的API
class CheckPreheatStatusView(LoginRequiredMixin, View):
    def get(self, request, pk):
        logger.info(f"CheckPreheatStatusView called for task {pk}")
        preheat = get_object_or_404(ManualPreheat, id=pk)
        logger.info(f"Task {pk} status: {preheat.status}, completed: {preheat.status == 'completed'}")
        response_data = {
            'status': preheat.status,
            'completed': preheat.status == 'completed',
            'failed_urls': preheat.failed_urls.split('\n') if preheat.failed_urls else [],
            'created_time': preheat.created_time.isoformat(),
            'completed_time': preheat.completed_time.isoformat() if preheat.completed_time else None
        }
        logger.info(f"CheckPreheatStatusView returning: {response_data}")
        return JsonResponse(response_data)

# 主页视图
class HomeView(LoginRequiredMixin, View):
    def get(self, request):
        # 获取统计数据
        server_count = Server.objects.count()
        domain_count = CDNDomain.objects.count()
        url_count = RequestUrl.objects.count()
        task_count = AutoTask.objects.count()
        
        # 添加消息提示
        messages = request.session.pop('messages', [])
        
        return render(request, 'preheat/home.html', {
            'server_count': server_count,
            'domain_count': domain_count,
            'url_count': url_count,
            'task_count': task_count,
            'custom_messages': messages if messages else None
        })

class RestartSchedulerView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        import logging
        from preheat.scheduler import restart_scheduler
        
        logger = logging.getLogger(__name__)
        logger.info("接收到重启调度器的请求")
        
        try:
            # 调用scheduler.py中的重启函数
            success, message = restart_scheduler()
            
            if success:
                logger.info("调度器重启成功")
                messages.success(request, message)
            else:
                logger.error(f"调度器重启失败: {message}")
                messages.error(request, message)
        except Exception as e:
            error_msg = f"重启调度器时发生异常: {str(e)}"
            logger.exception(error_msg)
            messages.error(request, error_msg)
        
        # 重定向回首页
        return redirect('preheat:home')

class GetResultDetailView(LoginRequiredMixin, View):
    def get(self, request, result_id):
        """获取任务执行结果的详细信息"""
        try:
            # 获取执行结果对象
            result = get_object_or_404(TaskExecutionResult, id=result_id)
            
            # 准备返回数据，使用与模板相同的时区处理方式
            from django.template.defaultfilters import date
            data = {
                'execution_time': date(result.execution_time, 'Y-m-d H:i:s'),
                'success': result.success,
                'success_count': result.success_count,
                'failed_count': result.failed_count,
                'failed_urls': result.failed_urls if result.failed_urls else None
            }
            
            return JsonResponse(data)
        except Exception as e:
            # 记录错误并返回错误响应
            import logging
            logger = logging.getLogger(__name__)
            logger.exception(f"获取执行结果详情失败: {str(e)}")
            return JsonResponse({'error': str(e)}, status=500)

# 用户管理视图
class UserListView(LoginRequiredMixin, ListView):
    model = User
    template_name = 'preheat/user_list.html'
    context_object_name = 'users'
    ordering = ['-date_joined']
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = '用户管理'
        return context

class UserCreateView(LoginRequiredMixin, CreateView):
    model = User
    form_class = UserCreationForm
    template_name = 'preheat/user_create.html'
    success_url = reverse_lazy('preheat:user_list')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = '创建新用户'
        context['action'] = 'create'
        return context
    
    def form_valid(self, form):
        messages.success(self.request, '用户创建成功！')
        return super().form_valid(form)

class UserUpdateView(LoginRequiredMixin, UpdateView):
    model = User
    form_class = UserUpdateForm
    template_name = 'preheat/user_update.html'
    success_url = reverse_lazy('preheat:user_list')
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = '更新用户信息'
        context['action'] = 'update'
        return context
    
    def form_valid(self, form):
        messages.success(self.request, '用户信息更新成功！')
        return super().form_valid(form)

class UserDeleteView(LoginRequiredMixin, DeleteView):
    model = User
    template_name = 'preheat/user_delete.html'
    success_url = reverse_lazy('preheat:user_list')
    
    def delete(self, request, *args, **kwargs):
        user = self.get_object()
        # 不允许删除当前登录用户
        if user == request.user:
            messages.error(request, '不能删除当前登录的用户！')
            return redirect('preheat:user_list')
        messages.success(request, f'用户 {user.username} 已成功删除！')
        return super().delete(request, *args, **kwargs)

class UserPasswordChangeView(LoginRequiredMixin, View):
    template_name = 'preheat/user_password_change.html'
    
    def get(self, request, pk):
        user = get_object_or_404(User, pk=pk)
        form = UserPasswordChangeForm(user)
        return render(request, self.template_name, {'form': form, 'user': user})
    
    def post(self, request, pk):
        user = get_object_or_404(User, pk=pk)
        form = UserPasswordChangeForm(user, request.POST)
        
        if form.is_valid():
            form.save()
            messages.success(request, f'用户 {user.username} 的密码已成功修改！')
            return redirect('preheat:user_list')
        
        return render(request, self.template_name, {'form': form, 'user': user})
